#pragma once
#include "DrawTexture.h"
#include "Vector2.h"
#include "Timer.h"


enum WrapMode
{
	WM_ONCE,
	WM_CLAMP,
	WM_LOOP
};

class SpriteAnimation : public DrawTexture
{
	GAMECOMPONENT( SpriteAnimation, DrawTexture )

	class AnimatonClip
	{
	public:
		AnimatonClip( const char* name, const Vector2& startPos, const Vector2 frameSize, uint_32 frames, uint_32 fps, WrapMode w, uint_32 layer = 0 )
			:	m_Name( name ), m_Start( startPos ), m_End( Vector2( startPos.x * frames, startPos.y ) ), m_FrameSize( frameSize ),
				m_Frames( frames ), m_Fps( fps ), m_WrapMode( w ), m_CurrentFrame( 0 ), m_Timer( .0f ), m_TheTimer( Boon::Timer::GetInstance() ),
				m_Layer( layer )
		{

		}

		inline void					ResetFrame() { m_CurrentFrame = 0; }
		inline float				GetLength() const { return m_Frames * ( 1.0f / m_Fps ); }
		inline bool					Update()
		{
			m_Timer += m_TheTimer.DeltaTime();
			if( m_Timer >= ( 1.0f / m_Fps ) )
			{
				m_Timer = .0f;
				if( m_WrapMode == WM_LOOP )
				{
					++m_CurrentFrame;
					if( m_CurrentFrame > m_Frames )
						m_CurrentFrame = 0;
					return true;
				}
				else if( m_WrapMode == WM_CLAMP )
				{
					m_CurrentFrame = Boon::Min( ++m_CurrentFrame, m_Frames );
					return true;
				}
				// once
				else if( ++m_CurrentFrame >= m_Frames )
				{
					m_CurrentFrame = 0;
					return false;
				}
			}
			return true;
		}

		inline Vector2			GetCurrentPos() const
		{
			return Vector2( m_Start.x + ( m_FrameSize.x * m_CurrentFrame ), m_Start.y );
		}

		const std::string		m_Name;
		const Vector2			m_Start;
		const Vector2			m_End;
		const Vector2			m_FrameSize;
		const uint_32			m_Frames;
		const uint_32			m_Fps;
		const WrapMode			m_WrapMode;
		const uint_32			m_Layer;
	private:
		uint_32					m_CurrentFrame;
		float					m_Timer;
		Boon::Timer&			m_TheTimer;
	};

	typedef std::pair< std::string, AnimatonClip>	MapPairAnimationClip;
	typedef std::map< std::string, AnimatonClip>	MapAnimationClips;

public:

	inline bool					CreateClip( const char* name, const Vector2& startPos, const Vector2& frameSize, uint_32 frames, uint_32 fps, WrapMode w = WM_LOOP, uint_32 layer = 0 )
	{
		auto result = m_AnimationClips.find( name );
		if( result != m_AnimationClips.end() )
			return false;

		;
		m_AnimationClips.insert( m_AnimationClips.begin(),
								 MapPairAnimationClip(	name,
														AnimatonClip(	name,
																		startPos,
																		frameSize,
																		frames - 1,
																		fps,
																		w,
																		layer ) ) );
		return true;
	}

	inline void					SetPause() { m_IsPaused = true; }
	inline bool					IsPlaying() const{ return !m_IsPaused; }
	inline void					Play() { if( m_CurrentClip ) m_IsPaused = false; }
	inline void					Play( const char* name )
	{
		auto result = m_AnimationClips.find( name );
		if( result != m_AnimationClips.end() )
		{
			// only play if layer of current playe clip is lower
			if( !m_CurrentClip || 
			  ( m_CurrentClip && m_CurrentClip->m_Layer <= result->second.m_Layer ) )
			{
				m_PreviousClip = m_CurrentClip;
				m_CurrentClip = &result->second;
				m_IsPaused = false;
			}
		}
	}
	inline void					Stop() { SetPause(); ResetClips(); m_CurrentClip = nullptr; }
	inline void					Stop( const char* name )
	{
		if( m_CurrentClip && m_CurrentClip->m_Name == name )
		{
			SetPause(); ResetClips(); m_CurrentClip = nullptr;
		}
	}

private:

	inline void					ResetClips()
	{
		auto iter = m_AnimationClips.begin();
		auto end = m_AnimationClips.end();
		while( iter != end )
		{
			iter->second.ResetFrame();
			++iter;
		}
	}

	virtual void		Update() 
	{
		DrawTexture::Update();
		if( !m_CurrentClip )
			return;

		if( IsPlaying() && !m_CurrentClip->Update() )
		{
			if( m_PreviousClip )
				m_CurrentClip = m_PreviousClip;
			SetPause();
		}
		Vector2 curPos = m_CurrentClip->GetCurrentPos();
		m_Node->SetArea( (uint_32)curPos.x, (uint_32)curPos.y, (uint_32)m_CurrentClip->m_FrameSize.x, (uint_32)m_CurrentClip->m_FrameSize.y );
	}

	virtual void		OnCreate() 
	{
		DrawTexture::OnCreate();
		m_IsPaused = true;
		m_CurrentClip = m_PreviousClip = nullptr;
	}

	virtual void		OnEnable() 
	{
		DrawTexture::OnEnable();
	}

	virtual void		OnDisable() 
	{
		DrawTexture::OnDisable();
	}

	virtual void		OnDestroy() 
	{
		DrawTexture::OnDestroy();
	}

	MapAnimationClips				m_AnimationClips;
	bool							m_IsPaused;
	AnimatonClip*					m_CurrentClip, *m_PreviousClip;
};


